package com.clw.runtime.heap;

import com.clw.runtime.Class;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Integer_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_InterfaceMethodref_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Long_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Methodref_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_NameAndType_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_String_info;
import com.sun.tools.classfile.ConstantPool.CPInfo;
import com.sun.tools.classfile.ConstantPoolException;

/**
 * Created by chengliwei on 2019/1/10.
 */
public class MethodArea {

    // 线程共享，与传统语言中的编译代码存储区或者操作系统进程的正文段的作用非常类似，它存储了每一个类的结构信息，
    // 例如，运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容，还包括一些在类、实例、接口初始化时用到的特殊方法

    // 方法区在虚拟机启动的时候创建，简单的虚拟机实现可以选择在这个区域不实现垃圾收集，容量可以固定也可以动态扩展或压缩，也可以实际内存中不连续
    // 方法区内存不足时抛出OutOfMemoryError


    public static class RuntimeConstantPool {

        private Object[] constants;
        private Class clazz;

        public Object[] getConstants() {
            return constants;
        }

        public void setConstants(Object[] constants) {
            this.constants = constants;
        }

        public Class getClazz() {
            return clazz;
        }

        public void setClazz(Class clazz) {
            this.clazz = clazz;
        }

        public RuntimeConstantPool(ConstantPool constantPool, Class clazz)
                throws ConstantPoolException {
            int constantsCount = constantPool.size();
            this.constants = new Object[constantsCount + 1];
            for (int i = 1; i < constantsCount; i++) {
                CPInfo cpInfo = constantPool.get(i);
                if (cpInfo instanceof CONSTANT_Integer_info) {
                    CONSTANT_Integer_info intInfo = (CONSTANT_Integer_info) cpInfo;
                    constants[i] = intInfo.value;
                } else if (cpInfo instanceof CONSTANT_Float_info) {
                    CONSTANT_Float_info floatInfo = (CONSTANT_Float_info) cpInfo;
                    constants[i] = floatInfo.value;
                } else if (cpInfo instanceof CONSTANT_Long_info) {
                    CONSTANT_Long_info longInfo = (CONSTANT_Long_info) cpInfo;
                    constants[i++] = longInfo.value;
                } else if (cpInfo instanceof CONSTANT_Double_info) {
                    CONSTANT_Double_info doubleInfo = (CONSTANT_Double_info) cpInfo;
                    constants[i++] = doubleInfo.value;
                } else if (cpInfo instanceof CONSTANT_String_info) {
                    CONSTANT_String_info stringInfo = (CONSTANT_String_info) cpInfo;
                    constants[i] = stringInfo.getString();
                } else if (cpInfo instanceof CONSTANT_Fieldref_info) {
                    CONSTANT_Fieldref_info fieldrefInfo = (CONSTANT_Fieldref_info) cpInfo;
                    CONSTANT_NameAndType_info nameAndTypeInfo = fieldrefInfo.getNameAndTypeInfo();
                    constants[i] = new FieldRef(this, fieldrefInfo.getClassName(),
                            nameAndTypeInfo.getName(), nameAndTypeInfo.getType());
                } else if (cpInfo instanceof CONSTANT_Class_info) {
                    CONSTANT_Class_info classInfo = (CONSTANT_Class_info) cpInfo;
                    constants[i] = new ClassRef(this, classInfo.getName());
                } else if (cpInfo instanceof CONSTANT_Methodref_info) {
                    CONSTANT_Methodref_info methodrefInfo = (CONSTANT_Methodref_info) cpInfo;
                    CONSTANT_NameAndType_info nameAndTypeInfo = methodrefInfo.getNameAndTypeInfo();
                    constants[i] = new MethodRef(this, methodrefInfo.getClassName(),
                            nameAndTypeInfo.getName(), nameAndTypeInfo.getType());
                } else if (cpInfo instanceof CONSTANT_InterfaceMethodref_info) {
                    CONSTANT_InterfaceMethodref_info interfaceMethodrefInfo = (CONSTANT_InterfaceMethodref_info) cpInfo;
                    CONSTANT_NameAndType_info nameAndTypeInfo = interfaceMethodrefInfo
                            .getNameAndTypeInfo();
                    constants[i] = new InterfaceMethodRef(this,
                            interfaceMethodrefInfo.getClassName(),
                            nameAndTypeInfo.getName(), nameAndTypeInfo.getType());
                }
            }
            this.clazz = clazz;
        }

        // 加载类或接口到虚拟机后，就创建对应的运行时常量池
    }

    public static class ClassMember {

        public ClassMember(int accessFlags, String name, String descriptor, Class clazz) {
            this.accessFlags = accessFlags;
            this.name = name;
            this.descriptor = descriptor;
            this.clazz = clazz;
        }

        protected int accessFlags;
        protected String name;
        protected String descriptor;
        protected Class clazz;

        public int getAccessFlags() {
            return accessFlags;
        }

        public void setAccessFlags(int accessFlags) {
            this.accessFlags = accessFlags;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getDescriptor() {
            return descriptor;
        }

        public void setDescriptor(String descriptor) {
            this.descriptor = descriptor;
        }

        public Class getClazz() {
            return clazz;
        }

        public void setClazz(Class clazz) {
            this.clazz = clazz;
        }
    }

    public static class Field extends ClassMember {

        public Field(int accessFlags, String name, String descriptor, Class containerClass) {
            super(accessFlags, name, descriptor, containerClass);
        }
    }

    public static class Method extends ClassMember {

        private int maxLocals;
        private int maxStack;
        private byte[] code;

        public Method(int accessFlags, String name, String descriptor, Class containerClass,
                int maxLocals, int maxStack, byte[] code) {
            super(accessFlags, name, descriptor, containerClass);
            this.maxLocals = maxLocals;
            this.maxStack = maxStack;
            this.code = code;
        }

        public int getMaxLocals() {
            return maxLocals;
        }

        public void setMaxLocals(int maxLocals) {
            this.maxLocals = maxLocals;
        }

        public int getMaxStack() {
            return maxStack;
        }

        public void setMaxStack(int maxStack) {
            this.maxStack = maxStack;
        }

        public byte[] getCode() {
            return code;
        }

        public void setCode(byte[] code) {
            this.code = code;
        }
    }
}
